home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / clang / cweb31.zip / EXAMPLES / TREEPRIN.W < prev    next >
Text File  |  1993-09-12  |  7KB  |  248 lines

  1. \def\covernote{Copyright 1987 Norman Ramsey -- Princeton University}
  2.  
  3. \def\vbar{\.{|}}
  4. @*Directory Trees.
  5. Our object is to print out a directory hierarchy in some pleasant way.
  6. The program takes output from {\tt find * -type d -print \vbar\ sort}
  7. @^system dependencies@>
  8. and produces a nicer-looking listing.
  9. More precisely, our input, which is the output of {\tt find} followed
  10. by {\tt sort}, is a list of fully qualified directory names (parent
  11. and child separated by slashes |'/'|); everything has already been
  12. sorted nicely into lexicographic order.
  13.  
  14. The {\tt treeprint} routine takes one option, |"-p"|, which tells it
  15. to use the printer's line-drawing set, rather than the terminal's.
  16.  
  17. @c
  18. @<Global definitions@>@;
  19. @<Global include files@>@;
  20. @<Global declarations@>@;
  21.  
  22. @#
  23. main(argc, argv)
  24.      int argc;
  25.      char **argv;
  26. {
  27. @<|main| variable declarations@>;
  28. @<Search for options and set special characters on |"-p"|@>;
  29. @<Read output from find and enter into tree@>;
  30. @<Write tree on standard output@>@;
  31. exit(0);
  32. }
  33.  
  34. @
  35. We make all the siblings of a directory a linked list off of its left child, 
  36. and the offspring a linked list off the right side.
  37. Data are just directory names.
  38. @d sibling left
  39. @d child right
  40.  
  41. @<Global decl...@>=
  42. typedef struct tnode {
  43.   struct tnode *left, *right;
  44.   char *data;
  45. } TNODE;
  46. @ @<|main| variable...@>=
  47. struct tnode *root;
  48.  
  49.  
  50.  
  51. @*Input.
  52. Reading the tree is simple---we read one line at a time, and call on the 
  53. recursive |add_tree| procedure.
  54.  
  55. @c
  56. read_tree (fp, rootptr)
  57.    FILE *fp;
  58.    struct tnode **rootptr;
  59. {
  60.    char buf[255], *p;
  61.  
  62.    while ((fgets(buf, 255, fp))!=NULL) {
  63.      @<If |buf| contains a newline, make it end there@>;
  64.      add_tree(rootptr, buf);
  65.    }
  66.  }
  67. @ @<Global include...@>=#include <stdio.h>
  68.  
  69. @ Depending what system you're on, you may or may not get a newline in |buf|.
  70. @<If |buf| contains a newline...@>=
  71.      p=buf; while (*p!='\0'&&*p!='\n') p++;
  72. @^system dependencies@>
  73.      *p='\0';
  74.  
  75. To add a string, we split off the first part of the name and insert it into 
  76. the sibling list. We then do the rest of the string as a child of the new node.
  77.  
  78. @c
  79. add_tree(rootptr, p)
  80.      struct tnode **rootptr;
  81.      char *p;
  82. {
  83.      char *s;
  84.      int slashed;
  85.  
  86.      if (*p=='\0') return;
  87.  
  88. @<Break up the string so |p| is the first word,
  89.      |s| points at null-begun remainder,
  90.     and |slashed| tells whether |*s=='/'| on entry@>;
  91.  
  92.      if (*rootptr==NULL) {
  93. @<Allocate new node to hold string of size |strlen(p)|@>;
  94.        strcpy((*rootptr)->data,p);
  95.      } 
  96.      if (strcmp((*rootptr)->data,p)==0) {
  97.            if (slashed) ++s;
  98.            add_tree(&((*rootptr)->child),s);
  99.      }
  100.        else {
  101.            if (slashed) *s='/';
  102.            add_tree(&((*rootptr)->sibling),p);
  103.      }
  104.    }
  105.  
  106. @ We perform some nonsense to cut off the string |p| so that |p| just
  107. holds the first word of a multiword name. Variable |s| points at what
  108. was either the end of |p| or a slash delimiting names. In either case
  109. |*s| is made |'\0'|.  Later, depending on whether we want to pass the
  110. whole string or the last piece, we will restore the slash or advance
  111. |s| one character to the right.
  112.  
  113. @<Break up...@>=
  114.      for (s=p;*s!='\0'&&*s!='/';) s++;
  115.      if (*s=='/') {
  116.        slashed=1;
  117.        *s='\0';
  118.      } else slashed=0;
  119.  
  120. @ Node allocation is perfectly standard \dots
  121. @<Allocate new node...@>=
  122.        *rootptr=(struct tnode *) malloc (sizeof(struct tnode));
  123.        (*rootptr)->left = (*rootptr)->right = NULL;
  124.        (*rootptr)->data = malloc (strlen(p)+1);
  125.  
  126. @
  127. @<Global decl...@>= char *malloc();
  128.  
  129. @ In this simple implementation, we just read from standard input.
  130. @<Read...@>= read_tree(stdin,&root);
  131.  
  132. @*Output.
  133. We begin by defining some lines, tees, and corners.
  134. The |s| stands for screen and the |p| for printer.
  135. You will have to change this for your line-drawing set.
  136. @^system dependencies@>
  137.  
  138. @<Global definitions@>=
  139. #define svert '|'
  140. #define shoriz '-'
  141. #define scross '+'
  142. #define scorner '\\' /* lower left corner */
  143.  
  144. #define pvert '|'
  145. #define phoriz '-'
  146. #define pcross '+'
  147. #define pcorner '\\' /* lower left corner */
  148.  
  149. @ The default is to use the terminal's line drawing set.
  150. @<Global declarations@>=
  151. char vert=svert;
  152. char horiz=shoriz;
  153. char cross=scross;
  154. char corner=scorner;
  155.  
  156. @ With option |"-p"| use the printer character set.
  157. @<Search for options...@>=
  158. while (--argc>0) {
  159.   if (**++argv=='-') {
  160.     switch (*++(*argv)) {
  161.       case 'p': 
  162.         vert=pvert;
  163.         horiz=phoriz;
  164.         cross=pcross;
  165.         corner=pcorner;
  166.     break;
  167.       default:
  168.         fprintf(stderr,"treeprint: bad option -%c\n",**argv);
  169.     break;
  170.       }
  171.   }
  172. }
  173.  
  174. @ We play games with a character stack to figure out when to put in vertical
  175. bars.
  176. A vertical bar connects every sibling with its successor, but the last sibling
  177. in a list is followed by blanks, not by vertical bars. The state of 
  178. bar-ness or space-ness for each preceding sibling is recorded in the 
  179. |indent_string| variable, one character (bar or blank) per sibling.
  180.  
  181. @<Global decl...@>=
  182. char indent_string[100]="";
  183.  
  184. @  Children get printed 
  185. before siblings.
  186. We don't bother trying to bring children up to the same line as their parents,
  187. because the \UNIX/ filenames are so long.
  188.  
  189. We define a predicate telling us when a sibling is the last in a series.
  190. @d is_last(S) (S->sibling==NULL)
  191.  
  192. @c
  193. print_node(fp, indent_string, node)
  194.      FILE *fp;
  195.      char *indent_string;
  196.      struct tnode *node;
  197. {
  198.   char string[255];
  199.      int i;
  200.      char *p, *is;
  201.  
  202.   if (node==NULL) {
  203.   }
  204.   else {
  205.     *string='\0';
  206.     for (i=strlen(indent_string); i>0; i--) 
  207.       strcat(string,@,      " |  ");
  208.     strcat(string,@t\ \ @>  " +--");
  209. @<Replace chars in |string| with chars from 
  210.          line-drawing set and from |indent_string|@>;
  211.     fprintf(fp,"%s%s\n",string,node->data);
  212.  
  213. @#
  214.     /* Add vertical bar or space for this sibling (claim |*is=='\0'|) */
  215.     *is++ = (is_last(node) ? ' ' : vert);
  216.     *is=='\0';
  217.    
  218.     print_node(fp, indent_string, node->child); /* extended |indent_string| */
  219.     *--is='\0';
  220.     print_node(fp, indent_string, node->sibling); /* original |indent_string| */
  221.   }
  222.  
  223. }
  224. @ For simplicity, we originally wrote connecting lines with |'|'|, |'+'|, and
  225. |'-'|.
  226. Now we replace those characters with appropriate characters from the 
  227. line-drawing set. 
  228. We take the early vertical bars and replace them with characters from
  229. |indent_string|, and we replace the other characters appropriately.
  230. We are sure to put a |corner|, not a |cross|, on the last sibling in 
  231. a group.
  232. @<Replace chars...@>=
  233.     is=indent_string;
  234.     for (p=string; *p!='\0'; p++) switch(*p) {
  235.        case '|': *p=*is++; break;
  236.        case '+': *p=(is_last(node) ? corner : cross); break;
  237.        case '-': *p=horiz; break;
  238.        default: break;
  239.            }
  240.  
  241.  
  242. @ For this simple implementation, we just write on standard output.
  243.  
  244. @<Write...@>= print_node(stdout, indent_string, root);
  245.  
  246. @*Index.
  247.